home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Language/OS - Multiplatform Resource Library
/
LANGUAGE OS.iso
/
a_utils
/
_archvrs
/
unix
/
tcx-1_0.lha
/
tcx-1.0
/
untcx.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-03-30
|
28KB
|
1,204 lines
/* untcx.c, Version 1.0, 25/3/1993 by Stewart Forster */
/************************************************************************/
/* Copyright (C) 1993 Stewart Forster */
/* This program is free software; you can redistribute it and/or modify*/
/* it under the terms of the GNU General Public License as published by*/
/* the Free Software Foundation; either version 2, or (at your option) */
/* any later version. */
/* */
/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
/* GNU General Public License for more details. */
/* */
/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/************************************************************************/
#include "config.h"
typedef struct path
{
char path[MAXPATHLEN]; /* Pathname to file */
int pid; /* Process id compressing file */
int local; /* Is file local? */
int ctime; /* Inode modification time */
struct path *next; /* Next structure */
} path;
#if defined(SUNOS)
#define MAXOPENFILES 4096
#define PIHASH(a, b) (((a) + (b)) % MAXOPENFILES)
typedef struct pstat
{
int dev;
int ino;
struct pstat *next;
} pstat;
pstat parr[MAXOPENFILES];
pstat *pihash[MAXOPENFILES];
void update_pstat_info();
#endif
extern int errno;
path *worklist = NULL, *freelist = NULL;
char logpath[MAXPATHLEN], logtmppath[MAXPATHLEN], lockpath[MAXPATHLEN];
void untcx_and_exec_local(char *, char *, char *[]);
void untcx_and_exec_nfs(char *, char *, char *, char *[]);
int try_to_exec(char *, char *[]);
void check_tcxd_mode();
int tcxd_reaper();
void just_untcx(char *, char *);
int scan_logtail(int);
int is_dir_local(char *);
int is_tcx(int);
int dodecode(int, int);
char *newargs[1024];
#ifdef ULTRIX
int usleep(int);
int usleep_sig();
#endif
int
main(int argc, char *argv[])
{
char cwd[MAXPATHLEN]; /* Save of current working directory */
char realdir[MAXPATHLEN]; /* Real directory packed executable lives in */
char execname[MAXPATHLEN]; /* Name of packed/target executable */
char execpath[MAXPATHLEN]; /* Full path name of packed executable */
char untcxtmp[MAXPATHLEN]; /* Temporary unpack pathname */
char tcxtarg[MAXPATHLEN]; /* Target path name of unpacked executable */
char *c; /* String manipulation pointers */
struct stat dostat; /* Stat buffer */
int i, local, isinterp;
if(argc < 2)
{
(void)fprintf(stderr, "Usage: %s filename\n", argv[0]);
exit(-1);
}
/* Re-exec if not root and pass -x along on the command line. */
/* If it's there when we restart and we're still not root, then */
/* avoid an exec loop by quitting with a warning. */
if(geteuid() != 0)
{
if(strcmp(argv[1], "-x") == 0)
{
(void)fprintf(stderr, "Error: untcx is not installed setuid!\n");
(void)fprintf(stderr, "Contact your systems administrator\n");
exit(-1);
}
/* Push -x into argv[1] and shift rest of args along */
newargs[0] = argv[0];
newargs[1] = "-x";
for(i = 1; i < argc; i++) newargs[i+1] = argv[i];
newargs[i + 1] = (char *)NULL;
if(execv(PATHUNTCX, newargs) < 0)
perror("exec");
/* exec failed! Warn user and quit */
(void)fprintf(stderr, "Unable to invoke interpreter as root\n");
exit(-1);
}
if(strcmp(argv[1], "-x") == 0)
{
for(i = 1; i < (argc - 1); i++) argv[i] = argv[i+1];
argv[i] = (char *)NULL;
--argc;
isinterp = 1;
}
else
if(strcmp(argv[1], "-u") == 0)
{
for(i = 1; i < (argc - 1); i++) argv[i] = argv[i+1];
argv[i] = (char *)NULL;
--argc;
isinterp = 0;
}
else
if(getuid() == 0)
isinterp = 1;
else
isinterp = 0;
/* Copy argv[1] to argv[0] for obsolete stuff (and IRIX) */
argv[0] = argv[1];
/* Setup to catch all undesirable signals */
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGHUP, SIG_IGN);
(void)signal(SIGTSTP, SIG_IGN);
(void)signal(SIGALRM, SIG_IGN);
(void)signal(SIGQUIT, SIG_IGN);
(void)signal(SIGTERM, SIG_IGN);
/* Set global paths */
(void)sprintf(logpath, "%s/log", ENFSDIR);
(void)sprintf(logtmppath, "%s/logtmp", ENFSDIR);
(void)sprintf(lockpath, "%s/.lock", ENFSDIR);
/* Check and start tcxd as required */
check_tcxd_mode();
/* Go to the uid of invoking user. Resolve through symbolic links */
/* as to what the real pathname of the executable is. */
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
/* Grab argv[0] and resolve to full path name via getwd() */
if(getwd(cwd) == NULL)
{
(void)fprintf(stderr, "Get Working Directory Error: %s\n", cwd);
exit(-1);
}
if(*argv[0] == '/')
(void)strcpy(realdir, argv[0]);
else
(void)sprintf(realdir, "%s/%s", cwd, argv[0]);
for(;;)
{
if((c = strrchr(realdir, '/')) == NULL)
{ /* Should never happen, BUT... */
(void)fprintf(stderr, "Help! Internal corruption of variables!\n");
exit(-1);
}
c++;
(void)strcpy(execname, c);
*c = '\0';
if(chdir(realdir) < 0) /* Oops. Failed. Report and quit. */
{
perror(realdir);
exit(-1);
}
if(getwd(cwd) == NULL)
{
(void)fprintf(stderr, "Get Working Directory Error: %s\n", cwd);
exit(-1);
}
/* Do lstat executable from here. If not a link, go on, else */
/* read the value of the link, and append it to realdir if */
/* it doesn't begin with a /, other just copy it to realdir */
/* and cycle through the loop again. */
if(lstat(execname, &dostat) < 0)
{
perror(execname);
exit(-1);
}
if((dostat.st_mode & S_IFMT) == S_IFLNK)
{
if(readlink(execname, execpath, MAXPATHLEN) < 0)
{
perror(execname);
exit(-1);
}
if(execpath[0] == '/')
(void)strcpy(realdir, execpath);
else
{
(void)strcat(realdir, "/");
(void)strcat(realdir, execpath);
}
continue;
}
if(!(dostat.st_mode & S_IFREG))
{
(void)fprintf(stderr, "Error: Not an executable file - %s/%s\n", realdir, execname);
exit(-1);
}
/* Now do a statfs on realdir. If it's on an NFS mounted filesystem we */
/* will need to unpack to ENFSDIR, otherwise we can unpack it in place, */
/* fsbuf.f_fstyp tells what filesystem type we're on. For IRIX this */
/* appears to be 1 for NFS, and 3 for local based filesystems. I could */
/* not find any documentation on this issue, so until a more portable */
/* method is found, I'll be using these two numbers. */
if((local = is_dir_local(realdir)) < 0)
exit(-1);
break;
}
/* Return to the original invocation directory */
if(chdir(cwd) < 0) /* Oops. Failed again! Try as user again! */
{
perror(cwd);
exit(-1);
}
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
/* Now check if last components of argv[0] and argv[1] are equal */
/* If not, we are invoked just to decompress something, not to */
/* execute it. Just attempt an uncompress as the user */
if(isinterp == 0)
{
#if defined(IRIX)
if(setuid(getuid()) < 0) { perror("setuid"); exit(-1); }
#else
if(setreuid(getuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
#endif
(void)sprintf(tcxtarg, "%s/%s", realdir, execname);
(void)sprintf(untcxtmp, "%s/.untcx.%s", realdir, execname);
just_untcx(tcxtarg, untcxtmp);
exit(0);
}
/* Try to do a local execute. If it returns, it means failure. */
if(local)
{
(void)sprintf(tcxtarg, "%s/%s", realdir, execname);
(void)sprintf(untcxtmp, "%s/.untcx.%s", realdir, execname);
untcx_and_exec_local(tcxtarg, untcxtmp, &(argv[1]));
}
/* We get this far if file is not local, or the local execution */
/* failed for some reason like shortage of disk space. Attempt */
/* to unpack program to /tmp and go from there. */
for(c = realdir; *c ; c++)
if(*c == '/')
*c = '=';
(void)sprintf(tcxtarg, "%s/%s/", ENFSDIR, realdir);
if(mkdir(tcxtarg, 0777) < 0)
if(errno != EEXIST)
{
perror(tcxtarg);
exit(-1);
}
(void)chmod(tcxtarg, 0777);
(void)strcat(tcxtarg, execname);
(void)sprintf(untcxtmp, "%s/%s/.untcx.%s", ENFSDIR, realdir, execname);
untcx_and_exec_nfs(argv[0], untcxtmp, tcxtarg, &(argv[1]));
/* :-( We only get this far if everything has failed. Exit with failure */
return(-1);
} /* main */
void
check_tcxd_mode()
{
char *s, spid[32];
int infd, logfd, lkfd, lasttime = 0, timer;
FILE *logfp;
path *curr, *prev;
struct stat sb; /* Stat buffer */
struct flock lck; /* File lock structure */
struct utimbuf times;
int lastoff;
#if defined(SUNOS)
pstat *pc; /* Hash/search pointer for SUNOS */
int found;
#endif
/* Check if we're root. If not, go home */
if(geteuid() != 0)
return;
/* Try to create emergency/NFS execute directory and set it's permissions */
/* It's not important yet if we can't at this stage. */
(void)mkdir(ENFSDIR, 01777);
(void)chmod(ENFSDIR, 01777);
/* Attempt to create ENFSDIR/.lock If can't, just return. */
if((lkfd = open(lockpath, O_CREAT | O_RDONLY, 0600)) < 0)
return;
/* Attempt to read lock ENFSDIR/.lock If can't, we assume the tcxd */
/* is already running. Return. */
lck.l_type = F_RDLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
if(fcntl(lkfd, F_SETLK, &lck) < 0)
{
(void)close(lkfd);
return;
}
/* Close lkfd and hence the lock on it, fork() and return */
(void)close(lkfd);
if(fork() != 0) /* On parent or fork error, just return */
return;
/* No errors to user at this point */
(void)fclose(stderr);
(void)close(2);
(void)open("/dev/null", O_WRONLY); /* Don't really care */
/* Make ourselves nice and rooted */
#if defined(IRIX)
if(setuid(geteuid()) < 0) exit(-1);
#else
if(setreuid(geteuid(), geteuid()) < 0) exit(-1);
#endif
/* Check if we're root again. If not, exit */
if(geteuid() != 0)
exit(-1);
/* Change directory to ENFSDIR to prevent hanging on NFS server crashes */
if(chdir(ENFSDIR) < 0)
exit(-1);
/* Lock the server lock file to prevent more than 1 starting up */
if((lkfd = open(lockpath, O_WRONLY | O_TRUNC)) < 0)
exit(-1);
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
if(fcntl(lkfd, F_SETLK, &lck) < 0)
exit(-1);
/* Write our process id to the lock file. Don't really care if fails. */
(void)sprintf(spid, "%d\n", getpid());
(void)write(lkfd, spid, strlen(spid));
/* setup our child process reaper */
(void)signal(SIGCHLD, tcxd_reaper);
/* Read in log file if it's there in case of crashes */
if((lastoff = scan_logtail(0)) < 0)
(void)creat(logpath, 0600);
/* Loop doing tasks at hand */
for(;;)
{
/* Before starting loop, logfp should be open for reading */
/* at end of log file. */
lastoff = 0;
for(timer = 0; timer < SCANRATE; timer++, (void)sleep(1))
{
if(stat(logpath, &sb) < 0)
continue;
/* Don't check if not updated, but do on last time */
if(sb.st_mtime <= lasttime)
continue;
lasttime = sb.st_mtime;
lastoff = scan_logtail(lastoff);
}
#if defined(SUNOS)
update_pstat_info();
#endif
for(prev = NULL, curr = worklist; curr != NULL; prev = curr, curr = curr->next)
{
/* Stat file. If not accessed within last 30 minutes */
/* we consider it a candidate for compression */
if(stat(curr->path, &sb) < 0)
{
(prev == NULL) ? (worklist = curr->next) : (prev->next = curr->next);
curr->next = freelist; freelist = curr;
(prev == NULL) ? (curr = worklist) : (curr = prev);
if(curr == NULL) break;
continue;
}
if(curr->local==1 && (time(NULL) - sb.st_atime) < LOCALTIMEOUT)
continue;
if(curr->local == 0 && (time(NULL) - sb.st_atime) < ENFSTIMEOUT)
continue;
/* Open file for reading and test if readlock exists */
/* If not, then if file is local, recompress it */
/* otherwise, unlink it from the ENFSDIR home */
#if defined(IRIX) || defined(DEC)
if((infd = open(curr->path, O_WRONLY)) < 0)
{
if(errno != ETXTBSY)
{
(prev == NULL) ? (worklist = curr->next) : (prev->next = curr->next);
curr->next = freelist; freelist = curr;
(prev == NULL) ? (curr = worklist) : (curr = prev);
if(curr == NULL) break;
continue;
}
times.actime = time(NULL);
times.modtime = sb.st_mtime;
(void)utime(curr->path, ×);
continue;
}
(void)close(infd);
#endif
#if defined(SUNOS)
found = 0;
for(pc = pihash[PIHASH(sb.st_dev, sb.st_ino)]; pc != NULL; pc = pc->next)
if(pc->dev == sb.st_dev && pc->ino == sb.st_ino)
{
times.actime = time(NULL);
times.modtime = sb.st_mtime;
(void)utime(curr->path, ×);
found = 1;
break;
}
if(found) continue;
#endif
if(curr->local == -1) /* Compression completed */
{
(prev == NULL) ? (worklist = curr->next) : (prev->next = curr->next);
curr->next = freelist; freelist = curr;
(prev == NULL) ? (curr = worklist) : (curr = prev);
if(curr == NULL) break;
continue;
}
if(curr->pid > 0) continue;
if(curr->local == 1)
{
/* Check inode modification times. If target is */
/* newer than what we know, forget it. */
if(sb.st_ctime > curr->ctime)
{
(prev == NULL) ? (worklist = curr->next) : (prev->next = curr->next);
curr->next = freelist; freelist = curr;
(prev == NULL) ? (curr = worklist) : (curr = prev);
if(curr == NULL) break;
continue;
}
/* Fork and compress program */
curr->pid = fork();
if(curr->pid != 0) continue;
/* Here we must be the child */
/* Close stdio stuff and reopen to /dev/null */
(void)close(2);
(void)close(1);
(void)close(0);
(void)open("/dev/null", O_RDONLY);
(void)open("/dev/null", O_WRONLY);
(void)open("/dev/null", O_WRONLY);
(void)execl(PATHTCX, "tcx", curr->path, (char *)0);
exit(-1);
}
/* At this point, file is in ENFSDIR. Delete it */
(void)unlink(curr->path);
s = strrchr(curr->path, '/');
*s = '\0';
(void)rmdir(curr->path);
(prev == NULL) ? (worklist = curr->next) : (prev->next = curr->next);
curr->next = freelist; freelist = curr;
(prev == NULL) ? (curr = worklist) : (curr = prev);
if(curr == NULL) break;
} /* for */
/* Update log file in case of crashes */
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
/* Do one final read in case someone has modified during */
/* the scan sequence. */
(void)scan_logtail(lastoff);
if((logfp = fopen(logtmppath, "w")) == NULL)
continue;
for(curr = worklist; curr != NULL; curr = curr->next)
(void)fprintf(logfp, "%s\n", curr->path);
(void)fclose(logfp);
/* Try for 20 times to lock file, sleep 5/100ths secs b/w tries */
for(timer = 0; timer < 20; timer++, PUSLEEP(50000))
{
if((logfd = open(logpath, O_WRONLY)) < 0)
continue;
if(fcntl(logfd, F_SETLK, &lck) < 0)
{ (void)close(logfd); continue; }
(void)rename(logtmppath, logpath);
(void)close(logfd);
break;
}
} /* for */
} /* check_tcxd_mode */
int
tcxd_reaper()
{
union wait state;
int pid;
path *curr;
while((pid = wait3(&state, WNOHANG, 0)) > 0)
for(curr = worklist; curr != NULL; curr = curr->next)
if(curr->pid == pid)
{
curr->pid = -1;
if(WIFEXITED(state) && WEXITSTATUS(state) == 0)
curr->local = -1;
break;
}
} /* tcxd_reaper */
void
untcx_and_exec_local(char *execpath, char *untcxtmp, char *argv[])
{
struct stat tostat; /* Stat buffer to check on lengths */
int owner, group;
int perms; /* Saved permissions to restore on target exec */
int infd, outfd; /* In and out file descriptors */
struct flock lck; /* File lock structure */
/* Stat executable and grab permissions */
if(stat(execpath, &tostat) < 0)
{
perror(argv[0]);
exit(-1);
}
perms = (tostat.st_mode & 0777);
owner = tostat.st_uid;
group = tostat.st_gid;
for(;;PUSLEEP(10000))
{
if((infd = open(execpath, O_RDWR)) < 0)
{
if(errno != ETXTBSY)
{
(void)fprintf(stderr, "Cannot write to %s\n", execpath);
exit(-1);
}
if((infd = open(execpath, O_RDONLY)) < 0)
continue;
(void)close(infd);
if(try_to_exec(execpath, argv) < 0)
exit(-1);
continue;
}
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
if(fcntl(infd, F_SETLK, &lck) < 0)
{
if(fcntl(infd, F_GETLK, &lck) < 0 || lck.l_type != F_RDLCK)
{ (void)close(infd); continue; }
(void)close(infd);
if(try_to_exec(execpath, argv) < 0)
exit(-1);
continue;
}
if(! is_tcx(infd))
{
(void)close(infd);
continue;
}
/* File is in packed format. Unpack it */
if((outfd = open(untcxtmp, O_WRONLY | O_CREAT | O_EXCL)) < 0)
{
if(errno != EEXIST)
{
perror(untcxtmp);
exit(-1);
}
if((outfd = open(untcxtmp, O_WRONLY | O_TRUNC)) < 0)
{
perror(untcxtmp);
exit(-1);
}
}
/* Start decompressing executable */
if(dodecode(infd, outfd) != 0)
{
if(unlink(untcxtmp) < 0)
perror("untcxtmp");
exit(-1);
}
(void)close(outfd);
/* Now set execute permissions on the beastie */
if(chmod(untcxtmp, perms) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror("untcxtmp");
exit(-1);
}
if(chown(untcxtmp, owner, group) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror("untcxtmp");
exit(-1);
}
/* Rename temporary file to target executable and close target */
if(rename(untcxtmp, execpath) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror("untcxtmp");
exit(-1);
}
(void)close(infd);
/* Everything OK! Now go exec the executable. */
if(try_to_exec(execpath, argv) < 0)
exit(-1);
} /* for */
} /* untcx_and_exec_local */
void
untcx_and_exec_nfs(char *execpath, char *untcxtmp, char *tcxtarg, char *argv[])
{
struct stat tostat; /* Stat buffer to check on lengths */
int mtime;
int owner, group;
int perms; /* Saved permissions to restore on target exec */
int infd, outfd; /* In and out file descriptors */
struct flock lck; /* File lock structure */
/* Stat executable and grab permissions */
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
if(stat(execpath, &tostat) < 0)
{
perror(argv[0]);
exit(-1);
}
perms = (tostat.st_mode & 0777);
mtime = tostat.st_mtime;
owner = tostat.st_uid;
group = tostat.st_gid;
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
for(;;PUSLEEP(10000))
{
if(stat(tcxtarg, &tostat) >= 0)
if(mtime > tostat.st_mtime)
(void)unlink(tcxtarg);
else
{
if(try_to_exec(tcxtarg, argv) < 0)
exit(-1);
continue;
}
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
if((infd = open(execpath, O_RDONLY)) < 0)
{
perror(execpath);
exit(-1);
}
if(setreuid(geteuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
if(! is_tcx(infd))
{
(void)close(infd);
if(try_to_exec(tcxtarg, argv) < 0)
exit(-1);
continue;
}
/* File is in packed format. Unpack it */
if((outfd = open(untcxtmp, O_EXCL | O_CREAT | O_WRONLY)) < 0)
{
if(errno != EEXIST)
{
perror(untcxtmp);
exit(-1);
}
if((outfd = open(untcxtmp, O_WRONLY | O_TRUNC)) < 0)
{
perror(untcxtmp);
exit(-1);
}
}
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
if(fcntl(outfd, F_SETLK, &lck) < 0)
{
(void)close(infd);
(void)close(outfd);
continue;
}
/* Start decompressing executable */
if(dodecode(infd, outfd) != 0)
{
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
(void)close(infd);
/* Now set execute permissions on the beastie */
if(chmod(untcxtmp, perms) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
if(chown(untcxtmp, owner, group) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
/* Rename temporary file to target executable and close target */
if(rename(untcxtmp, tcxtarg) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
(void)close(outfd);
/* Everything OK! Now go exec the executable. */
if(try_to_exec(tcxtarg, argv) < 0)
exit(-1);
} /* for */
} /* untcx_and_exec_nfs */
void
just_untcx(char *execpath, char *untcxtmp)
{
struct stat tostat; /* Stat buffer to check on lengths */
int owner, group;
int perms; /* Saved permissions to restore on target exec */
int infd, outfd; /* In and out file descriptors */
struct flock lck; /* File lock structure */
/* Stat executable and grab permissions */
if(stat(execpath, &tostat) < 0)
{
perror(execpath);
exit(-1);
}
perms = (tostat.st_mode & 0777);
owner = tostat.st_uid;
group = tostat.st_gid;
if((infd = open(execpath, O_RDWR)) < 0)
{
perror(execpath);
exit(-1);
}
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
if(fcntl(infd, F_SETLK, &lck) < 0)
{
perror(execpath);
exit(-1);
}
if(! is_tcx(infd))
{
(void)fprintf(stderr, "File does not appear to be in tcx format. Aborting\n");
exit(-1);
}
/* File is in packed format. Unpack it */
if((outfd = open(untcxtmp, O_WRONLY | O_CREAT | O_EXCL)) < 0)
{
if(errno != EEXIST)
{
perror(untcxtmp);
exit(-1);
}
if((outfd = open(untcxtmp, O_WRONLY | O_TRUNC)) < 0)
{
perror(untcxtmp);
exit(-1);
}
}
/* Start decompressing executable */
if(dodecode(infd, outfd) != 0)
{
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
(void)close(outfd);
/* Now set execute permissions on the beastie */
if(chmod(untcxtmp, perms) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
if(chown(untcxtmp, owner, group) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
/* Rename temporary file to target executable and close target */
if(rename(untcxtmp, execpath) < 0)
{
perror(untcxtmp);
if(unlink(untcxtmp) < 0)
perror(untcxtmp);
exit(-1);
}
(void)close(infd);
return;
} /* untcx_and_exec_local */
int
try_to_exec(char *execpath, char *argv[])
{
int infd, try;
int logfd;
FILE *logfp;
struct flock lck;
if((infd = open(execpath, O_RDONLY)) < 0)
{
if(errno == ENOENT)
{
perror(execpath);
return -1;
}
return 0;
}
if(is_tcx(infd))
{
(void)close(infd);
return 0;
}
lck.l_type = F_WRLCK; lck.l_whence = 0; lck.l_start = 0; lck.l_len = 0;
for(try = 0; try < 10; try++, PUSLEEP(50000))
{
if((logfd = open(logpath, O_WRONLY)) < 0)
continue;
if(fcntl(logfd, F_SETLK, &lck) < 0)
{
(void)close(logfd);
continue;
}
if((logfp = fopen(logpath, "a+")) == NULL)
{
(void)close(logfd);
continue;
}
(void)fprintf(logfp, "%s\n", execpath);
(void)fclose(logfp);
(void)close(logfd);
break;
}
#if defined(IRIX)
if(setuid(getuid()) < 0) { perror("setuid"); exit(-1); }
#else
if(setreuid(getuid(), getuid()) < 0) { perror("setreuid"); exit(-1); }
#endif
/* Reset signals back to normal to prevent carriage through execv */
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGHUP, SIG_DFL);
(void)signal(SIGTSTP, SIG_DFL);
(void)signal(SIGALRM, SIG_DFL);
(void)signal(SIGQUIT, SIG_DFL);
(void)signal(SIGTERM, SIG_DFL);
(void)close(infd);
if(execv(execpath, argv) < 0)
perror(execpath);
return -1;
} /* try_to_exec */
#if defined(SUNOS)
void
update_pstat_info()
{
FILE *fp;
char line[256], tmp[10];
pstat *curr;
int i, pos;
int maj, min, dev, ino, cnt;
for(pos = 0 ; pos < MAXOPENFILES; pos++)
pihash[pos] = (pstat *)NULL;
if((fp = popen(PSTATI, "r")) == NULL)
return;
if(fgets(line, 256, fp) == NULL)
return;
for(i = 0; fgets(line, 256, fp) != NULL && i < MAXOPENFILES; i++)
{
curr = parr + i;
strncpy(tmp, line+16, 3); tmp[3] = '\0'; maj = atoi(tmp);
strncpy(tmp, line+20, 3); tmp[3] = '\0'; min = atoi(tmp);
strncpy(tmp, line+23, 6); tmp[6] = '\0'; ino = atoi(tmp);
dev = maj * 256 + min;
pos = PIHASH(dev, ino);
curr->dev = dev;
curr->ino = ino;
curr->next = pihash[pos];
pihash[pos] = curr;
} /* while */
parr[i].dev = parr[i].ino = -1;
pclose(fp);
} /* update_pstat_info */
#endif
int
scan_logtail(int lastoff)
{
char newpath[MAXPATHLEN], *s;
FILE *logfp;
path *curr;
struct stat sb;
if(lastoff < 0)
lastoff = 0;
if((logfp = fopen(logpath, "r")) == NULL)
return -1;
(void)fseek(logfp, lastoff, SEEK_SET);
while(fgets(newpath, MAXPATHLEN, logfp) != NULL)
{
for(s = newpath; *s; s++) if(*s == '\n') *s = '\0';
for(curr = worklist; curr != NULL; curr = curr->next)
if(strcmp(curr->path, newpath) == 0) break;
if(curr != NULL)
continue;
if(stat(newpath, &sb) < 0)
continue;
if(freelist != NULL)
{
curr = freelist;
freelist = freelist->next;
}
else
if((curr = (path *)malloc(sizeof(path))) == NULL)
continue;
(void)strcpy(curr->path, newpath);
curr->pid = -1;
(strstr(newpath, ENFSDIR) == newpath) ? (curr->local = 0) : (curr->local = 1);
curr->ctime = sb.st_ctime;
curr->next = worklist;
worklist = curr;
} /* while */
lastoff = ftell(logfp);
(void)fclose(logfp);
return lastoff;
} /* scan_logtail */
#ifdef ULTRIX
int
is_dir_local(char *dir)
{
struct fs_data fsbuf;
if(statfs(dir, &fsbuf) < 1) /* Returns 0 on "NOT MOUNTED" */
return -1;
/* NFS Version 2 returns -1 or 0 for both gfree, and gtot */
/* to a client, so return false on this condition. */
if((fsbuf.fd_req.gfree <= 0) && (fsbuf.fd_req.gtot <= 0))
return 0;
return 1;
} /* is_dir_local */
#else
int
is_dir_local(char *dir)
{
struct statfs fsbuf; /* Statfs buffer to check local versus NFS executable */
if(statfs(dir, &fsbuf, (int)(sizeof(struct statfs)), 0) < 0)
return -1;
/* NFS Version 2 returns -1 or 0 for both f_files, and f_ffree */
/* to a client, so return false on this condition. */
if((fsbuf.f_files <= 0) && (fsbuf.f_ffree <= 0))
return 0;
return 1;
} /* is_dir_local */
#endif
int
is_tcx(int fd)
{
int i;
unsigned char c;
for(i = 0; i < 256; i++)
if(read(fd, &c, 1) < 1 || c == 0)
break;
if((i >= 256) || read(fd, &c, 1) < 1 || c != 76 || read(fd, &c, 1) < 1 || c != 193
|| read(fd, &c, 1) < 1 || c != 13 || read(fd, &c, 1) < 1 || c != 138 )
return 0;
return 1;
} /* is_tcx */
int
dodecode(int infd, int outfd)
{
int pid;
union wait status;
pid = fork();
if(pid < 0) return -1;
if(pid == 0)
{
if(dup2(infd, 0) < 0) exit(-1); /* Attach infd to stdin */
(void)close(infd);
if(dup2(outfd, 1) < 0) exit(-1); /* Attach outfd to stdout */
(void)close(outfd);
#ifdef UNPACKOPTS
(void)execl(PATHUNPACK, "unpacker", UNPACKOPTS, (char *)0);
#else
(void)execl(PATHUNPACK, "unpacker", (char *)0);
#endif
exit(-1);
}
else
pid = wait(&status);
return WEXITSTATUS(status);
} /* dodecode */
#ifdef ULTRIX
int
usleep(int usec)
{
struct itimerval value, dumb;
(void)signal(SIGALRM, usleep_sig);
if(getitimer(ITIMER_REAL, &value) != 0)
{
perror("Error: couldn't get timer");
return(-1);
}
value.it_value.tv_sec = value.it_interval.tv_sec = (long) usec / 1000000;
value.it_value.tv_usec = value.it_interval.tv_usec = (long) usec % 1000000;
if(setitimer(ITIMER_REAL, &value, &dumb) != 0) /* ignore parameter 3 */
{
perror("Error: couldn't set timer");
return(-1);
}
(void)sigpause(SIGALRM);
if(getitimer(ITIMER_REAL, &value) != 0)
{
perror("Error: couldn't get timer");
return(-1);
}
usec = (value.it_value.tv_sec - value.it_interval.tv_sec) * 1000000;
usec += (value.it_value.tv_usec - value.it_interval.tv_usec);
return usec;
}
int
usleep_sig()
{
} /* usleep_sig */
#endif